import java.util.*;
import java.io.*;
import qp.utils.*;
import java.util.Vector;

public class RandomDB
{

	private static Random random;

	static int[][] pk;// ******************in order to support multi- attributes
						// to be a primary key
	public static int pkRange = 1;// *********************the pkRange product
	public static int pkNum = 0;// ***********************the number of
								// attributes to form a primary key
	public static int pkPointer = 0;

	public static Vector fk = new Vector();// ***********************store the
											// reference pk(keyinfo) file

	public RandomDB()
	{
		random = new Random(System.currentTimeMillis());
	}

	/** Generates a random string of length equal to range **/

	public String randString(int range)
	{

		String s = "";
		for (int j = 0; j < range; j++)
			s += (new Character((char) (97 + random.nextInt(26)))).toString();
		return s;
	}

	public static void main(String[] args)
	{

		RandomDB rdb = new RandomDB();

		if (args.length != 2)
		{
			System.out.println("Usage: java RandomDB <dbname> <numrecords> ");
			System.exit(1);
		}
		String tblname = args[0];
		String srcfile = args[0] + ".det";
		String metafile = args[0] + ".md";
		String datafile = args[0] + ".txt";
		String statfile = args[0] + ".stat";
		String keyinfofile = args[0] + ".keyinfo"; // *************************************add
													// keyinfofile to store the
													// infomation about primary
													// key
		int numtuple = Integer.parseInt(args[1]);

		try
		{
			BufferedReader in = new BufferedReader(new FileReader(srcfile));
			ObjectOutputStream outmd = new ObjectOutputStream(
					new FileOutputStream(metafile));
			PrintWriter outtbl = new PrintWriter(new BufferedWriter(
					new FileWriter(datafile)));
			PrintWriter outstat = new PrintWriter(new BufferedWriter(
					new FileWriter(statfile)));

			outstat.print(numtuple);
			outstat.println();
			/** first line is <number of columns> **/

			String line = in.readLine();
			int numCol = Integer.parseInt(line);
			String[] datatype = new String[numCol];
			int[] range = new int[numCol];
			String[] keytype = new String[numCol];

			/** second line is <size of tuple = number of bytes> **/
			line = in.readLine();
			int size = Integer.parseInt(line);
			// outstat.print(size);
			// outstat.println();

			/**
			 * Capture information about data types, range and primary/foreign
			 * keys
			 **/
			/** format is <colname><coltype><range><keytype><attrsize> **/

			/** for schema generation **/
			Vector attrlist = new Vector();
			Attribute attr;
			boolean flag = false;
			int i = 0;

			while ((line = in.readLine()) != null)
			{
				StringTokenizer tokenizer = new StringTokenizer(line);
				int tokenCount = tokenizer.countTokens();
				/** get column name **/
				String colname = tokenizer.nextToken();

				/** get data type **/
				datatype[i] = tokenizer.nextToken();

				int type;
				if (datatype[i].equals("INTEGER"))
				{
					type = Attribute.INT;
					// System.out.println("integer");
				} else if (datatype[i].equals("STRING"))
				{
					type = Attribute.STRING;
					// System.out.println("String");
				} else if (datatype[i].equals("REAL"))
				{
					type = Attribute.REAL;
				} else if (datatype[i].equals("TIME"))
				{ // *************************************************modify
					// here if add time type
					type = Attribute.TIME;
				} else
				{
					type = -1;
					System.err.println("invalid data type");
					System.exit(1);
				}

				/** range of the values allowed **/
				range[i] = Integer.parseInt(tokenizer.nextToken());

				/** key type PK/FK/NK/PF **/
				keytype[i] = tokenizer.nextToken();
				String keytypeReserve = keytype[i];
				// System.out.println("ooooooooooooooooo"+keytype[i].substring(2,
				// 3));
				if (keytype[i].length() > 2)// ***PF#..
				{
					keytype[i] = keytype[i].substring(0, 2);
					// System.out.println("tttttttttttttt"+keytype[i]);
				}
				int typeofkey;
				if (keytype[i].equals("PK"))
				{
					pkNum++;
					if (range[i] >= 1)
						pkRange = range[i] * pkRange;
					// pk = new boolean[range[i]];
					typeofkey = Attribute.PK;

				} else if (keytype[i].equals("PF"))
				{
					pkNum++;
					if (range[i] >= 1)
						pkRange = range[i] * pkRange;

					// fkNum++;
					// if(range[i]>fkRange)
					// fkRange=range[i];
					fk.add(keytypeReserve.substring(3, keytypeReserve.length()));

					typeofkey = Attribute.PF;
				} else if (keytype[i].equals("FK"))
				{
					// fkNum++;
					// if(range[i]>fkRange)
					// fkRange=range[i];
					fk.add(keytypeReserve.substring(3, keytypeReserve.length()));

					typeofkey = Attribute.FK;
				} else
				{
					typeofkey = -1;
				}
				int numbytes = Integer.parseInt(tokenizer.nextToken());

				if (typeofkey != -1)
				{
					attr = new Attribute(tblname, colname, type);// ,typeofkey,numbytes);
				} else
				{
					attr = new Attribute(tblname, colname, type, typeofkey);
				}
				attr.setAttrSize(numbytes);
				attrlist.add(attr);
				i++;
			}
			Schema schema = new Schema(attrlist);
			schema.setTupleSize(size);
			outmd.writeObject(schema);// store schema in .md file
			outmd.close();

			pk = new int[pkNum][pkRange];// *****************************pk
			// fk=new boolean[fkNum][fkRange];//*************************fk
			for (i = 0; i < numtuple; i++)
			{
				// System.out.println("in table generation: "+i);
				/*
				 * int numb = random.nextInt(range[0]);// generate pk value
				 * (randomly) while(pk[numb]==true){ numb =
				 * random.nextInt(range[0]); } pk[numb]=true;
				 * outtbl.print(numb+"\t");//only support the first value of the
				 * tuple to be primary key ,this value type must be INTEGER
				 */

				int fkPointer = 0;
				int curpknum = 0;// *****************************************
				int[] curpk = new int[pkNum];// *****************************store
												// one tuple's PK info
				String tuple = "";
				for (int j = 0; j < numCol; j++)
				{

					if (datatype[j].equals("STRING"))
					{
						String temp = rdb.randString(range[j]);
						// outtbl.print(temp+"\t");
						tuple = tuple + temp + "\t";
					} else if (datatype[j].equals("REAL"))
					{ // bug here......modify from float to real
						float value = range[j] * random.nextFloat(); // ????
						// outtbl.print(value+"\t");
						tuple = tuple + value + "\t";
					} else if (datatype[j].equals("TIME"))
					{ // ***************************************add time type
						String time;
						String year = "" + (2010 + (int) (Math.random() * 2)); // get
																				// 2010
																				// or
																				// 2011
						String month = "" + (1 + (int) (Math.random() * 12));
						String day = "" + (1 + (int) (Math.random() * 30));
						String hour = "" + (1 + (int) (Math.random() * 24));
						String minute = "" + (1 + (int) (Math.random() * 60));
						time = year + "-" + month + "-" + day + "*" + hour
								+ ":" + minute;// yyyy-MM-dd*HH:mm
						// outtbl.print(time+"\t");
						tuple = tuple + time + "\t";
					} else if (datatype[j].equals("INTEGER"))
					{
						if ((keytype[j].equals("FK"))
								|| (keytype[j].equals("PF")))
						{
							// int value = random.nextInt(range[j]);

							// System.out.println("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"+(String)fk.elementAt(fkPointer));
							BufferedReader inKeyinfo = new BufferedReader(
									new FileReader((String) fk
											.elementAt(fkPointer) + ".keyinfo"));
							fkPointer++;
							int numOfpkTuples = Integer.parseInt(inKeyinfo
									.readLine());
							int RandomNum = 1 + (int) (Math.random() * numOfpkTuples);
							int value = 0;
							for (int h = 0; h < RandomNum; h++)
							{
								value = Integer.parseInt(inKeyinfo.readLine());
							}
							tuple = tuple + value + "\t";
							if (keytype[j].equals("PF"))
							{
								curpk[curpknum] = value;
								curpknum++;
							}
							inKeyinfo.close();
							// fk[value]=true;
						} else if (keytype[j].equals("PK"))
						{
							int value = random.nextInt(range[j]);
							tuple = tuple + value + "\t";
							curpk[curpknum] = value;
							curpknum++;
						} else if (keytype[j].equals("NK"))
						{
							int value = random.nextInt(range[j]);
							tuple = tuple + value + "\t";
						}
					}

				}

				boolean existSamePK = false;
				for (int n = 0; n < pkPointer; n++) // ***************check
													// Primary key
				{
					int m;
					for (m = 0; m < pkNum; m++)
					{
						if (pk[m][n] != curpk[m])
							break;
					}
					if (m == (pkNum))// -1)&&(pk[pkNum-1][n]==curpk[pkNum-1]))
					{
						existSamePK = true;
						break;
					}
				}

				if (existSamePK)
				{
					i--;
				}
				if (!existSamePK)
				{
					outtbl.print(tuple);
					for (int e = 0; e < pkNum; e++)
					{
						pk[e][pkPointer] = curpk[e];
					}
					pkPointer++;
				}
				if ((i != numtuple - 1) && (!existSamePK))
					outtbl.println();

			}
			outtbl.close();

			// System.out.println("end of table generation");
			/**
			 * printing the number of distinct values of each column in
			 * <tablename>.stat file
			 **/

			for (i = 0; i < numCol; i++)
			{
				if (datatype[i].equals("STRING"))
				{
					outstat.print(numtuple + "\t");
				} else if (datatype[i].equals("REAL"))
				{ // ....modify from FLOAT to REAL
					outstat.print(numtuple + "\t");
				} else if (datatype[i].equals("INTEGER"))
				{
					if (keytype[i].equals("PK"))
					{
						// int numdist = rdb.getnumdistinct(pk);
						int numdist = numtuple;// ***********************
						outstat.print(numdist + "\t");
					} else if (keytype[i].equals("FK")||keytype[i].equals("PF"))//just calculate this 
					{
						BufferedReader inTXT = new BufferedReader(
								new FileReader(datafile));
						boolean[] FKStat=new boolean[range[i]+1]; 
						
						for(int q=0;q<=range[i];q++)
						{
							FKStat[q]=false;
						}
						
						try{
							for(int p=0;p<numtuple;p++)
							{
								String tuple=inTXT.readLine();
								StringTokenizer tokenizer = new StringTokenizer(tuple,"\t");
								String FKnum="";
								for(int t=0;t<=i;t++)
								{
									FKnum = tokenizer.nextToken();
								}
								int FKvalue = Integer.parseInt(FKnum);
								FKStat[FKvalue]=true;
							}
						}
						catch(Exception e){
							
						}
						int numdist=0;
						for(int r=0;r<=range[i];r++)
						{
							if(FKStat[r]==true)
								numdist++;
						}
						inTXT.close();
						// int numdist = rdb.getnumdistinct(fk);
						//int numdist = numtuple;
						outstat.print(numdist + "\t");
					} else
					{
						if (numtuple < range[i])
							outstat.print(numtuple + "\t");
						else
							outstat.print(range[i] + "\t");
					}

				} else if (datatype[i].equals("TIME"))
				{ // ************************************modify as add time type
					outstat.print(numtuple + "\t");
				}
			}
			outstat.close();
			in.close();

			// ***********************************************if the PK include
			// one attribute, then write primary key info to keyinfofile
			if (pkNum == 1)
			{
				PrintWriter outkeyinfo = new PrintWriter(new BufferedWriter(
						new FileWriter(keyinfofile)));// ****************write
														// keyinfofile to store
														// primary key
				outkeyinfo.println(pkPointer);
				for (int q = 0; q < pkPointer; q++)
				{
					outkeyinfo.println(pk[0][q]);
				}
				outkeyinfo.close();
			}

		} catch (IOException io)
		{
			System.out.println("error in IO ");
			System.exit(1);
		}

	}
	/*
	 * public int getnumdistinct(boolean[] key){ int lenght=key.length; int
	 * count=0; for(int i=0;i<lenght;i++){ if(key[i]==true) count++; } return
	 * count; }
	 */
}
